<template>
  <a-select 
    :value="accountValue" 
    :mode="mode" 
    :show-search="showSearch" 
    @search="onSearch"
    @deselect="onDeselect"
    @dropdownVisibleChange="dropdownVisibleChange" 
    :getPopupContainer="getPopupContainer"
  >
    <div slot="dropdownRender" slot-scope="menu">
      <v-nodes :vnodes="menu" />
      <div 
        class="ant-select-dropdown-menu-item" 
        :class="value.includes(item[optionValueKey]) ? 'ant-select-dropdown-menu-item-selected' : ''" 
        v-for="item in list" :key="item[optionValueKey]" 
        @click="customChange(item[optionValueKey])" 
        @mousedown="e => e.preventDefault()"
      >
        {{item[optionTitleKey]}}
        <a-icon class="anticon anticon-check ant-select-selected-icon" type="check" />
      </div>
      <div class="placeholder" :style="'height: '+ placeholderHeight +'px;'"></div>
    </div>
    <div slot="notFoundContent"></div>
  </a-select>
</template>

<script>
import { deepCopyArray,  } from '@/utils/util'
export default {
  props: {
    value: [Array],
    mode: String,
    showSearch: {
      type: Boolean,
      default: false
    },
    dataSource: {
      type: Array,
      default: () => []
    },
    optionTitleKey: {
      type: String,
      default: 'title',
    },
    optionValueKey: {
      type: String,
      default: 'value',
    },
    filterOption: [Boolean, Function],
  },
  data() {
    return {
      accountValue:[],
      list: [], //数据
      viewCount: 30, // 展示的数量
      viewHeight: 32, // 每行高度
      moveBaes: 6, // 迁移基数
      moveCount: 0, // 迁移次数
      backgroundEl:null, // 占位背景el
      placeholderHeight: 0, // 占位背景高度
      scrollTop: 0,
      direction: '',
      timeout: null,
      menu: null,
      dropdown: null,
      keywords: '',
    }
  },
  created() {
    this.setAccountValue(this.value)
  },
  mounted() {
    this.setPopupContainer()
  },
  methods: {
    init() {
      setTimeout(() => { // 防止悬浮框定位bug
        // 初始化设置：设置占位div，事件监听
        this.create()
      }, 0);
    },
    create() {  
      // 设置占位背景,设置前清除内容
      this.menu = document.querySelector('.v-select').querySelector(".ant-select-dropdown-content")
      let el = document.querySelector('.v-select').querySelector(".ant-select-dropdown-menu") // 要清除的el
      this.createList() // 初始化数据
      this.addEvent() // 添加事件监听
      if (el && el.parentNode) {
        el.parentNode.remove()
      }
    },
    popupScroll(e) {
      // placeholderHeight 等于0时，滑到底部，不再滑动
      if(this.placeholderHeight == 0){return}
      // 滑动时调用
      // console.log(this.menu.scrollTop, e.srcElement.scrollTop, e.preventDefault);
      let scrollTop = e.srcElement.scrollTop
      // 确定滑动方向
      let direction;
      if (scrollTop >= this.scrollTop) {
        direction = 'down'
      } else {
        direction = 'up'
      }
      this.scrollTop = scrollTop
      this.direction = direction
      // console.log(direction, scrollTop);
      // 计算信息
      const h = this.viewCount * this.viewHeight  // 一块的整体高度
      const section = Math.floor(this.scrollTop / h) // 移动过了多少个区间
      const index = Math.floor((this.scrollTop - h * section) / this.viewHeight)  //当前scrollTop在该区间的位置
      // 获取位置等信息
      let arr = this.getInfo(section, index, h, this.dataSource)
      // console.log(arr);  
      // 渲染
      const viewList = document.querySelector('.v-select').querySelectorAll(".ant-select-dropdown-menu-item")
      
      const list = new Array(this.viewCount)
      for (let idx = 0; idx < this.viewCount; idx++) { 
        let item = this.dataSource[arr[idx]['idx']] 
        list[idx] = item
        viewList[idx].style.transform = arr[idx]['translateY']
      }
      this.list = deepCopyArray(list)
      let placeholderHeight = Math.floor(this.dataSource.length * this.viewHeight) - ((section + 1) * this.viewCount * this.viewHeight)
      this.placeholderHeight = placeholderHeight >= 0 ? placeholderHeight : 0
    },
    getInfo(section, index, h, data) {
      // 有以下几种情况
      // A、B、index、C、D
      // B、C为可视区域（o1）、B有可能超出A 到 D（o2）、C 有可能超出 D 到 A（o3）
      // console.log(range);
      // section == 0 向下滑 mod1: section, mod2:section, mod3: section + 1, mod4: section + 1, mod5: section
      // section == 0 向上滑 mod1: section, mod2:section, mod3: section + 1, mod4: section, mod5: section
      // section > 0 向下滑 mod1: section, mod2:section, mod3: section + 1, mod4: section + 1, mod5: section
      // section > 0 向上滑 mod1: section, mod2:section - 1, mod3: section+1, mod4: section, mod5: section - 1
      const count = 8 // 前后各8的范围
      const result = new Array(this.viewCount)
      let range = this.getRange(index, count)
      for (let idx = 0; idx < this.viewCount; idx++) { 
        switch (range[idx]) {
          case 'mod2':
          case 'mod5':
            if (section > 0 && this.direction == 'up') {
              let b = section - 1 >= 0 ? section - 1 : 0
              result[idx] = this.setObject(b, idx, h, data)
            } else {
              result[idx] = this.setObject(section, idx, h, data)
            }
            break;
          case 'mod3':
          case 'mod4':
            if (section == 0 || this.direction == 'down') {
              result[idx] = this.setObject(section + 1, idx, h, data)
            } else {
              result[idx] = this.setObject(section + 1, idx, h, data)
            }
            break;
          default:
            result[idx] = this.setObject(section, idx, h, data)
            break;
        }
      }
      return result
    },
    getRange(index, count) {
      // 取范围
      let res = new Array(this.viewCount)
      // mod1 // 可视区域
      // mod2 // 上超出区域
      // mod3 // 下超出区域
      // mod4 // 上部到可视的区域
      // mod5 // 下部到可视的区域
      let start = index - count
      let end = index + count
      for (let idx = start; idx <= end; idx++){
        if (idx < 0) {
          res[this.viewCount + idx] = 'mod2'
        } else if (idx > this.viewCount - 1) {
          res[idx - this.viewCount] = 'mod3'
        } else {
          res[idx] = 'mod1'
        }
      }
      if (start > 0) {
        for (let idx = 0; idx < start; idx++){
          res[idx] = res[idx] ? res[idx] : 'mod4'
        }
      }
      if (end < this.viewCount - 1) {
        for (let idx = end + 1; idx < this.viewCount; idx++){
          res[idx] = res[idx] ? res[idx] : 'mod5'
        }
      }
      return res
    },
    setObject(section, idx, h, data) {
      const count = data.length
      let i = section * this.viewCount + idx
      i = i - Math.floor(i / count) * count
      return {
        translateY: "translateY(" + section * h + "px)",
        idx: i,
      }
    },
    dropdownVisibleChange(open) {
      // 展开下拉菜单的回调
      if (open) {
        this.init()
      } else {
        this.keywords = ''
      }
    },
    setPopupContainer() {
      // 设置放置位置
      if (!document.querySelector('.v-select')) {
        let div = document.createElement('div')
        div.className = 'v-select'
        document.body.querySelector('#popContainer').appendChild(div)
      }
    },
    addEvent() {
      // 事件冒泡，模拟hover，废弃：组件原生事件bug修复
      // this.menu.addEventListener('mouseover', this.mouseenterEvent)
      // this.menu.addEventListener('mouseout', this.mouseleaveEvent)

      this.dropdown = document.querySelector('.v-select').querySelector(".ant-select-dropdown")
      this.dropdown.style.maxHeight = '250px'
      this.dropdown.style.overflowY = 'scroll'
      this.dropdown.addEventListener('scroll', this.popupScroll, { passive: true })
    },
    removeEvent() {
      let el = document.querySelector('.v-select').querySelector(".ant-select-dropdown")
      el.removeEventListener('scroll', this.popupScroll)
    },
    createList() {
      // 回到顶部
      if (this.dropdown) {
        this.dropdown.scrollTop = 0
      }
      // copy数据,设置list
      if (this.keywords === '') {
        // 设置占位高度
        this.placeholderHeight = Math.floor(this.dataSource.length * this.viewHeight)
        this.list = this.dataSource.slice(0, this.viewCount)
      } else {
        // 设置占位高度
        this.placeholderHeight = 0
        this.list = this.dataSource.filter(e => e[this.optionTitleKey].indexOf(this.keywords) >= 0)
      }
      this.$nextTick(() => {
        const viewList = document.querySelector('.v-select').querySelectorAll(".ant-select-dropdown-menu-item")
        for (let item of viewList) {
          item.style.transform = "translateY(" + 0 + "px)"
        }
      })
      // console.log(this.list, deepCopyArray(this.dataSource.slice(0, this.viewCount)))
    },
    customChange(val) {
      // 要是 this.value 中存在就删除，不存在就添加
      const value = deepCopyArray(this.value)
      if (value.includes(val)) {
        let idx = value.findIndex(e => e == val)
        value.splice(idx, 1)
      } else {
        value.push(val)
      }
      this.onChange(value)
    },
    onDeselect(account) {
      // 取消选中，账户名
      let item = this.dataSource.find(e => e[this.optionTitleKey] == account)
      if (item) {
        const value = deepCopyArray(this.value)
        const key = item[this.optionValueKey]
        let idx = value.findIndex(id => id == key)
        value.splice(idx, 1)
        this.onChange(value)
      }
    },
    onChange(value) {
      // console.log(value);
      this.$emit("input", value)
      this.$emit("change", value)
    },
    setAccountValue(val) {
      // 设置展示型value,展示optionTitleKey
      this.accountValue = val.map(id => {
        let item = this.dataSource.find(o => o.id == id)
        return item[this.optionTitleKey]
      })
    },
    onSearch(value) {
      this.keywords = value
      this.init()
    },
    mouseenterEvent(e) {
      const normalizeE = e || window.event
      const target = normalizeE.target || normalizeE.srcElement
      if (target.className == 'ant-select-dropdown-menu-item') {
        target.classList.add('ant-select-dropdown-menu-item-active')
      }
    },
    mouseleaveEvent(e) {
      const normalizeE = e || window.event
      const target = normalizeE.target || normalizeE.srcElement
      if (target.className == 'ant-select-dropdown-menu-item ant-select-dropdown-menu-item-active') {
        target.classList.remove('ant-select-dropdown-menu-item-active')
      }
    },
    getPopupContainer() {
      return document.querySelector('.v-select')
    },
  },
  watch: {
    value(val) {
      this.setAccountValue(val)
    },
  },
  components: {
    VNodes: {
      functional: true,
      render: (h, ctx) => ctx.props.vnodes,
    },
  },
}
</script>

<style lang="less" scoped>

</style>